/* Linux epoll(2) based ae.c module
 *
 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Redis nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


#include <sys/epoll.h>

typedef struct aeApiState {
    int epfd;
    struct epoll_event *events;
} aeApiState;

static int aeApiCreate(aeEventLoop *eventLoop) {
    aeApiState *state = zmalloc(sizeof(aeApiState));

    if (!state) return -1;
    // 也就是说aeApiState 的events指针也是指向一个数组 长度跟aeEventLoop的文本事件数量一样
    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);
    if (!state->events) {
        zfree(state);
        return -1;
    }
    //主要功能是创建一个epoll文件描述符，该文件描述符用于后续的epoll操作
    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
    if (state->epfd == -1) {
        zfree(state->events);
        zfree(state);
        return -1;
    }
    eventLoop->apidata = state;
    return 0;
}

static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
    aeApiState *state = eventLoop->apidata;

    state->events = zrealloc(state->events, sizeof(struct epoll_event)*setsize);
    return 0;
}

static void aeApiFree(aeEventLoop *eventLoop) {
    aeApiState *state = eventLoop->apidata;

    close(state->epfd);
    zfree(state->events);
    zfree(state);
}

static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
    aeApiState *state = eventLoop->apidata;
    struct epoll_event ee = {0}; /* avoid valgrind warning */
    /* If the fd was already monitored for some event, we need a MOD
     * operation. Otherwise we need an ADD operation. */
    int op = eventLoop->events[fd].mask == AE_NONE ?
            EPOLL_CTL_ADD : EPOLL_CTL_MOD;

    ee.events = 0;
    mask |= eventLoop->events[fd].mask; /* Merge old events */
    if (mask & AE_READABLE) ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
    ee.data.fd = fd;
    //  epoll_ctl()添加的事件的就绪状态 epoll_wait()获取ctl添加的事件
    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
    return 0;
}

static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {
    aeApiState *state = eventLoop->apidata;
    struct epoll_event ee = {0}; /* avoid valgrind warning */
    int mask = eventLoop->events[fd].mask & (~delmask);

    ee.events = 0;
    if (mask & AE_READABLE) ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
    ee.data.fd = fd;
    if (mask != AE_NONE) {
        epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee);
    } else {
        /* Note, Kernel < 2.6.9 requires a non null event pointer even for
         * EPOLL_CTL_DEL. */
        epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee);
    }
}

static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
    aeApiState *state = eventLoop->apidata;
    int retval, numevents = 0;

  /*
    epoll_wait (int __epfd, struct epoll_event *__events,  int __maxevents, int __timeout);
    epfd (epoll file descriptor):
    这是一个由 epoll_create 或 epoll_create1 创建的 epoll 实例的文件描述符。

    通过这个描述符，你可以向 epoll 实例中添加、删除或修改要监视的文件描述符。
    events (pointer to epoll_event structure array):
    这是一个指向 epoll_event 结构数组的指针，用于接收准备就绪的事件。 [出参] 就绪事件数组

    epoll_event 结构包含与事件相关的文件描述符和数据。
    在调用 epoll_wait 后，events 数组会被填充为准备就绪的文件描述符和它们关联的事件（例如读就绪、写就绪等）。
    maxevents (maximum number of events to return):
    这个参数指定了 events 数组可以容纳的最大事件数。
    epoll_wait 最多会返回这个数目的准备就绪事件。如果少于这个数目的事件准备就绪，那么实际返回的事件数会少于 maxevents。
    如果这个参数设置为 0，那么 epoll_wait 将立即返回，不等待任何事件发生。
    timeout (maximum time to wait):
    这个参数指定了 epoll_wait 在没有事件准备就绪时应等待的最长时间（以毫秒为单位）。
    如果设置为 -1，epoll_wait 将无限期地等待，直到至少有一个事件准备就绪。
    如果设置为 0，epoll_wait 将立即返回，不等待任何事件发生。
    如果设置为一个正整数 N，epoll_wait 将等待最多 N 毫秒。如果在这段时间内没有事件准备就绪，它将返回一个错误。

    ✅ state->events在初始化时分配内存，但内容为空
2.2.
✅ epoll_ctl添加事件到内核监控表，不涉及 state->events
3.3.
✅ epoll_wait阻塞等待，当有事件时就绪时内核填充 state->events数组
4.4.
✅ Redis 随后遍历数组处理就绪事件

    */
    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
    if (retval > 0) {
        int j;

        numevents = retval;
        for (j = 0; j < numevents; j++) {
            int mask = 0;
            struct epoll_event *e = state->events+j;
/*#define EPOLLIN     0x001   // 可读数据
#define EPOLLOUT    0x004   // 可写数据
#define EPOLLERR    0x008   // 错误发生
#define EPOLLHUP    0x010   // 挂起/对端关闭*/
            if (e->events & EPOLLIN) mask |= AE_READABLE;
            // if 条件成立执行后面的按位或操作 0 按位或（bitwise OR with 0）的结果非常直接：任何数与 0 进行按位或操作，结果都是该数本身。
            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
            if (e->events & EPOLLERR) mask |= AE_WRITABLE;
            if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
            eventLoop->fired[j].fd = e->data.fd;
            eventLoop->fired[j].mask = mask;
        }
    }
    return numevents;
}

static char *aeApiName(void) {
    return "epoll";
}
